#include "MyNativeModule.h"

#include <stdexcept>

#include "utils/Logger.h"

namespace test_native_module {
MyNativeModule::MyNativeModule(int x) : x(x) {
  addFunc("testFunc", std::bind(&MyNativeModule::testFunc, this, std::placeholders::_1, std::placeholders::_2),
          MethodType::SYNC);
  addFunc("testMax", std::bind(&MyNativeModule::testMax, this, std::placeholders::_1, std::placeholders::_2),
          MethodType::SYNC);
}

std::string MyNativeModule::getName() { return "myNativeModule"; }

std::vector<MethodDescriptor> MyNativeModule::getMethods() { return methodNames_; }

Dynamic MyNativeModule::getConstants() {
  std::map<std::string, Dynamic> obj;
  obj["x"] = Dynamic((int64_t)1);
  obj["y"] = Dynamic(std::string("y"));
  return Dynamic(obj);
}

void MyNativeModule::invokeSync(unsigned int methodId, std::vector<Dynamic>& args) {
  logInfo("calling invoke in MyNativeModule method: " + std::to_string(methodId) + "\n");
  if (methodId >= methodNames_.size()) {
    throw std::runtime_error("method not found");
  }
  assert(methodNames_[methodId].type == MethodType::SYNC);
  auto funcName = methodNames_[methodId].name;
  if (funcMap_.find(funcName) == funcMap_.end()) {
    throw std::runtime_error("method not found");
  }
  auto val1 = args[0].getType() == Dynamic::Type::Double ? args[0].getDouble() : args[0].getInt64();
  auto val2 = args[1].getType() == Dynamic::Type::Double ? args[1].getDouble() : args[1].getInt64();
  auto ret = funcMap_[funcName](val1, val2);
  args.push_back(ret);

  // check if callback is needed
  if (callbacks_.find(funcName) != callbacks_.end()) {
    callbacks_[funcName](val1, val2);
  }
}

void MyNativeModule::invokeAsync(unsigned int methodId, std::vector<Dynamic>& args, unsigned int callbackId) {
  logError("async unsupported in this module\n");
  abort();
}

void MyNativeModule::addFunc(std::string name, FunctionType func, MethodType funcType) {
  funcMap_[name] = func;
  methodNames_.push_back(MethodDescriptor(name, funcType));
}

void MyNativeModule::addCallback(std::string name, Callback callback) { callbacks_[name] = callback; }

Dynamic MyNativeModule::testFunc(int x, int y) {
  logInfo("testFunc called with param x: " + std::to_string(x) + ", y:" + std::to_string(y) + "\n");
  return Dynamic();
}

Dynamic MyNativeModule::testMax(int x, int y) {
  logInfo("testMax called with param: x: " + std::to_string(x) + ", y: " + std::to_string(y) + "\n");
  auto ret = x > y ? x : y;
  logInfo("testMax returned: " + std::to_string(ret) + "\n");
  return Dynamic((int64_t)ret);
}
}  // namespace test_native_module
